<?php

namespace App\service\order;

use Exception;
use support\Log;
use AdaPay\AdaPay;
use support\Redis;
use think\facade\Db;
use PhpAmqpLib\Wire\AMQPTable;
use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Connection\AMQPStreamConnection;
use crmeb\exceptions\AdminException;
use think\db\exception\{DataNotFoundException, DbException, ModelNotFoundException};

include_once app_path() . '/../' . 'extend/AdapaySdk/init.php';
include_once app_path() . '/../' . 'extend/checkstand/class/H5Cashier.php';

class StoreOrderServices
{
    public function __construct()
    {
        AdaPay::init(app_path() . '/../' . '/extend/AdapayDemo/config/config.json', "live", false);
    }

    /**
     * 订单查询
     * @param string $orderCode 订单号
     * @return array|string
     */
    public function findQuery(string $orderCode)
    {
        $client = new \H5Cashier;

        // 参数, 每次需要重新赋值
        $client->body = array(
            'orderCode' => $orderCode, //订单号
            'extend' => ''
        );
        // 返回结果
        $ret = $client->request('orderQuery');
        $ret['data'] = json_decode($ret['data'], true);
        return $ret;
    }

    /**
     * 藏品下单
     * @param int $uid 下单用户id
     * @param int $goods_id 藏品id
     * @param int $buy_count 购买个数
     * @param string $pay_type 支付方式 sd_h5_checkstand 杉德H5收银台支付  hf 汇付支付
     * @param int $order_pk_id 订单主键id 重新付款的时候 传入的重新下单的订单主键id
     * @return array
     * @throws Exception
     */
    public function orderCreate(int $uid, int $goods_id, int $buy_count, string $pay_type, int $order_pk_id = 0)
    {
        $is_cx_buy = $order_pk_id ? 1 : 0;
        $detection = $this->detectionCount($uid, $goods_id, $buy_count, $is_cx_buy);

        $redis_user_info = $detection['redis_user_info'];
        $redis_goods_info = $detection['redis_goods_info'];

        $prefix = env('REDIS_PREFIX');
        $ttl_seconds = env('REDIS_TTL_SECONDS');
        $key = $prefix . $goods_id . '_ex_goods_id_num';

        $stock = Redis::get($key);

        if ($stock === null) {
            throw new AdminException('库存暂未开启！');
        }

        if (!$order_pk_id) {
            if ($stock === '0') {
                throw new AdminException('库存不足！');
            }

            $current_have_num = Db::name('goods')->where('id', $goods_id)->value('current_have_num');

            if ($current_have_num <= 0) {
                throw new AdminException('库存不足！');
            }

            $value_key = $prefix . 'goods_' . $goods_id . '_number';
            $value_number = Redis::get($value_key);
            if (!$value_number) {
                $value_number = 0;
            }

            if ($value_number == $redis_goods_info['num']) {
                throw new AdminException('库存不足！');
            }


            $res = Redis::decrBy($key, $buy_count); // 将 key 中 储存的数字值增一，如果key不存在，那么key的值会先被初始化为0，然后再执行 incr操作
        } else {
            $res = 1;
        }

        if ($res >= 0) {
            // 秒杀成功

            //开始事务
            Db::startTrans();

            try {
                $random = uniqid();
                $orderCode = $this->orderId() . $random;
                $again_key = $prefix . $goods_id . '_again_buy_num';

                // 开始创建订单
                if ($order_pk_id) {

                    $getPayment = Db::name('store_order')->where('id', $order_pk_id)->find();

                    if (!$getPayment) {
                        throw new AdminException('发起重新支付的订单号不存在！');
                    }
//                    if ($getPayment['status'] == 4) {
//                        throw new AdminException('订单已超时！');
//                    }

                    if ($getPayment['status'] != 4 && strtotime($getPayment['txn_time_out']) <= time()) {
                        throw new AdminException('订单已过付款有效期！');
                    }

                    Redis::incrBy($again_key, $getPayment['buy_count']);
                }

                $txn_time_out = time() + 300;
                $result = [];

                switch ($pay_type) {
                    case 'sd_h5_checkstand':
                        // 杉德支付
                        $client = new \H5Cashier;
                        // 参数
                        $totalAmount = intval($redis_goods_info['price'] * 100);
                        $totalAmount = intval($buy_count * $totalAmount);
                        $var = sprintf("%012d", $totalAmount);
                        $client->body = array(
                            'orderCode' => $orderCode,
                            'totalAmount' => $var,
                            'subject' => '购买藏品',
                            'body' => '用户购买藏品消费',
                            'notifyUrl' => env('PAY_NOTIFY_URL') . '/orderApi/payNotify',
                            'frontUrl' => env('PAY_NOTIFY_URL') . '/orderApi/targetHome',
                            'userId' => substr($redis_user_info['mobile'], -10),
//                            'txnTimeOut' => date('YmdHis', $txn_time_out),
//                            'payModeList' => "[qppay,qrpay,h5ibank,alipay,wechatpay,unionpay,wxjsbridge,wxjsapi,alijsbridge,alijsapi]"
                        );
                        // 生成表单
                        $form = $client->form('orderCreate');
                        $result = $form;
                        break;
                }

                // 购买 使用 nft_list表 的 主键id 这个id  到异步的队列里面  再去分配
                $sqlData = [
                    'order_id' => $orderCode, // 订单号
                    'uid' => $uid, // 用户id
                    'account_id' => $redis_user_info['account_id'], // 购买人链账户id
                    'mobile' => $redis_user_info['mobile'], // 用户手机号
                    'goods_id' => $goods_id, // 藏品ID
                    'goods_name' => $redis_goods_info['name'], // 藏品名称
                    'total_num' => $buy_count, // 订单商品总数
                    'buy_count' => $buy_count, // 购买数量
                    'total_price' => bcmul((string)$redis_goods_info['price'], (string)$buy_count, 2), // 订单总价
                    'pay_price' => bcmul((string)$redis_goods_info['price'], (string)$buy_count, 2), // 实际支付金额
                    'txn_time_out' => date('YmdHis', $txn_time_out),
                    'created_at' => date('Y-m-d H:i:s'),
                    'updated_at' => date('Y-m-d H:i:s')
                ];

                if (strtotime($redis_goods_info['issuer_date']) > time()) {
                    $sqlData['is_yxg'] = 1;
                }

                $order_auto_id = Db::name('store_order')->insertGetId($sqlData);

                if ($order_pk_id) {
                    Db::name('store_order')->where('id', $order_pk_id)->update([
                        'status' => 4,
                        'updated_at' => date('Y-m-d H:i:s')
                    ]);
                }

                // 提交事务
                Db::commit();
            } catch (\Exception $e) {
                // 回滚事务
                Db::rollback();
                Redis::incrBy($key, $buy_count);
                throw new AdminException($e->getMessage());
            }

            // 发送 订单查询 延时队列
            $this->sendSelectOrderMsg($order_auto_id . ':1', 120000); // 2分钟
            $this->sendSelectOrderMsg($order_auto_id . ':2', 290000); // 4分50秒
            $this->sendSelectOrderMsg($order_auto_id . ':3', 350000); // 5分50秒
            $this->sendSelectOrderMsg($order_auto_id . ':4', 900000); // 15分
            $this->sendSelectOrderMsg($order_auto_id . ':5', 1800000);// 30分
            $this->sendSelectOrderMsg($order_auto_id . ':6', 3600000);// 60分

            return [
                'form' => $result,
                'order_id' => $orderCode
            ];

        } else {
            throw new AdminException('抢购失败');
        }
    }

    /**
     * 藏品下单 压力测试
     * @param int $uid 下单用户id
     * @param int $goods_id 藏品id
     * @param int $buy_count 购买个数
     * @param string $pay_type 支付方式 sd_h5_checkstand 杉德H5收银台支付  hf 汇付支付
     * @param int $order_pk_id 订单主键id 重新付款的时候 传入的重新下单的订单主键id
     * @return array
     * @throws Exception
     */
    public function orderCreateTest(int $uid, int $goods_id, int $buy_count, string $pay_type, int $order_pk_id = 0)
    {
        $is_cx_buy = $order_pk_id ? 1 : 0;
        $detection = $this->detectionCount($uid, $goods_id, $buy_count, $is_cx_buy);

        $redis_user_info = $detection['redis_user_info'];
        $redis_goods_info = $detection['redis_goods_info'];

        $prefix = env('REDIS_PREFIX');
        $ttl_seconds = env('REDIS_TTL_SECONDS');
        $key = $prefix . $goods_id . '_ex_goods_id_num';

        $stock = Redis::get($key);

        if ($stock === null) {
            throw new AdminException('库存暂未开启！');
        }

        if ($stock === '0') {
            throw new AdminException('库存不足！');
        }

        $res = Redis::decrBy($key, $buy_count); // 将 key 中 储存的数字值增一，如果key不存在，那么key的值会先被初始化为0，然后再执行 incr操作

        if ($res >= 0) {
            // 秒杀成功

            //开始事务
            Db::startTrans();

            try {
                $random = uniqid();
                $orderCode = $this->orderId() . $random;
                $again_key = $prefix . $goods_id . '_again_buy_num';

                // 开始创建订单
                if ($order_pk_id) {

                    $getPayment = Db::name('store_order')->where('id', $order_pk_id)->find();

                    if (!$getPayment) {
                        throw new AdminException('发起重新支付的订单号不存在！');
                    }
                    if ($getPayment['status'] == 4) {
                        throw new AdminException('订单已超时！');
                    }

                    if ($getPayment['status'] != 4 && strtotime($getPayment['txn_time_out']) <= time()) {
                        throw new AdminException('订单已过付款有效期！');
                    }

                    Redis::incrBy($again_key, $getPayment['buy_count']);
                }

                $txn_time_out = time() + 300;
                $result = [];

                switch ($pay_type) {
                    case 'sd_h5_checkstand':
                        // 杉德支付
                        $client = new \H5Cashier;
                        // 参数
                        $totalAmount = intval($redis_goods_info['price'] * 100);
                        $totalAmount = intval($buy_count * $totalAmount);
                        $var = sprintf("%012d", $totalAmount);
                        $client->body = array(
                            'orderCode' => $orderCode,
                            'totalAmount' => $var,
                            'subject' => '购买藏品',
                            'body' => '用户购买藏品消费',
                            'notifyUrl' => env('PAY_NOTIFY_URL') . '/orderApi/payNotify',
                            'frontUrl' => env('PAY_NOTIFY_URL') . '/orderApi/targetHome',
                            'userId' => substr($redis_user_info['mobile'], -10),
//                            'txnTimeOut' => date('YmdHis', $txn_time_out),
//                            'payModeList' => "[qppay,qrpay,h5ibank,alipay,wechatpay,unionpay,wxjsbridge,wxjsapi,alijsbridge,alijsapi]"
                        );
                        // 生成表单
                        $form = $client->form('orderCreate');
                        $result = $form;
                        break;
                }

                // 购买 使用 nft_list表 的 主键id 这个id  到异步的队列里面  再去分配
                $sqlData = [
                    'order_id' => $orderCode, // 订单号
                    'uid' => $uid, // 用户id
                    'account_id' => $redis_user_info['account_id'], // 购买人链账户id
                    'mobile' => $redis_user_info['mobile'], // 用户手机号
                    'goods_id' => $goods_id, // 藏品ID
                    'goods_name' => $redis_goods_info['name'], // 藏品名称
                    'total_num' => $buy_count, // 订单商品总数
                    'buy_count' => $buy_count, // 购买数量
                    'total_price' => bcmul((string)$redis_goods_info['price'], (string)$buy_count, 2), // 订单总价
                    'pay_price' => bcmul((string)$redis_goods_info['price'], (string)$buy_count, 2), // 实际支付金额
                    'txn_time_out' => date('YmdHis', $txn_time_out),
                    'created_at' => date('Y-m-d H:i:s'),
                    'updated_at' => date('Y-m-d H:i:s')
                ];

                if (strtotime($redis_goods_info['issuer_date']) > time()) {
                    $sqlData['is_yxg'] = 1;
                }

                $order_auto_id = Db::name('store_order')->insertGetId($sqlData);

                if ($order_pk_id) {
                    Db::name('store_order')->where('id', $order_pk_id)->update([
                        'status' => 4,
                        'updated_at' => date('Y-m-d H:i:s')
                    ]);
                }

                // 提交事务
                Db::commit();
            } catch (\Exception $e) {
                // 回滚事务
                Db::rollback();
                Redis::incrBy($key, $buy_count);
                throw new AdminException($e->getMessage());
            }

            // 发送 订单查询 延时队列
            $this->sendSelectOrderMsg($order_auto_id . ':1', 120000); // 2分钟
            $this->sendSelectOrderMsg($order_auto_id . ':2', 290000); // 4分50秒
            $this->sendSelectOrderMsg($order_auto_id . ':3', 350000); // 5分50秒
            $this->sendSelectOrderMsg($order_auto_id . ':4', 900000); // 15分
            $this->sendSelectOrderMsg($order_auto_id . ':5', 1800000);// 30分
            $this->sendSelectOrderMsg($order_auto_id . ':6', 3600000);// 60分

            $this->sendGoodsNotifyTestMsg($order_auto_id, 60000); // 模拟1分钟后支付成功通知回调

            return [
                'form' => $result,
                'order_id' => $orderCode
            ];

        } else {
            throw new AdminException('抢购失败');
        }
    }

    /**
     * 杉德收银台H5支付回调
     * @param array $result
     * @return bool
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function payNotify(array $result)
    {
        $pos = json_encode($result, JSON_UNESCAPED_UNICODE);
        Log::info('支付通知返回post:' . $pos);

        if (empty($pos)) {
            throw new AdminException('杉德支付返回信息有误');
        }

        if (!isset($result['data'])) {
            throw new AdminException('杉德支付返回信息有误');
        }

        $data = $result['data'];
        $sign = $result['sign'];

        $client = new \H5Cashier;
        $data = stripslashes($data); //支付数据

        // 验签
        $verifyFlag = $client->verify($data, $sign);
        if (!$verifyFlag) {
            throw new AdminException('签名失败');
        }
        if (!empty($data)) {
            $data = json_decode($data, true);
        }
        $trade_no = $data['body']['tradeNo']; // 拿到订单号

        $orderInfo = Db::name('store_order')->where('order_id', $trade_no)->find();

        if (!$orderInfo) {
            throw new AdminException("订单不存在！");
        }

        $goods_id = $orderInfo['goods_id'];
        $uid = $orderInfo['uid'];
        // 订单 用户信息
        $orderUser = Db::name('user')->where('id', $uid)->find();

        // 藏品信息
        $redis_goods_info = $this->getGoodsInfo($orderInfo['goods_id']);

        if ($orderInfo['status'] == 1 || $orderInfo['status'] == 2) {
            throw new AdminException("订单已支付！");
        }

        if ($orderInfo['status'] == 4) {
            throw new AdminException("订单已超时！");
        }

        // 用户信息
        $redis_user_info = $this->getUserInfo($orderInfo['uid']);

//        if (empty($redis_user_info['account_id'])) {
//            //throw new AdminException('您暂未区块链账户，请去实名认证页面查看并获取！');
//        }

        $prefix = env('REDIS_PREFIX');
        //$ttl_seconds = env('REDIS_TTL_SECONDS');

        $find = Db::name('nft')->where('uid', $uid)->where('goods_id', $redis_goods_info['id'])->find();

        //开始事务
        Db::startTrans();

        try {
//            $key = $prefix . $goods_id . '_ex_goods_id_num';
            // 编号 key redis计数
            $value_key = $prefix . 'goods_' . $goods_id . '_number';
            $buy_count = $orderInfo['buy_count']; // 购买数量

            # 减库存
            if (!Db::name('goods')->where('id', $goods_id)->dec('current_have_num', $buy_count)->update()) {
                throw new AdminException("网络错误请稍后重试！");
            }

            // 加已售数量
            if (!Db::name('goods')->where('id', $goods_id)->inc('sold', $buy_count)->update()) {
                throw new AdminException("网络错误请稍后重试！");
            }

            if (!$find) {
                $nft_pk_id = Db::name('nft')->insertGetId([
                    'uid' => $uid,
                    'mobile' => $redis_user_info['mobile'],
                    'account_id' => $redis_user_info['account_id'],
                    'goods_id' => $redis_goods_info['id'],
                    'goods_name' => $redis_goods_info['name'],
                    'created_at' => date('Y-m-d H:i:s'),
                    'updated_at' => date('Y-m-d H:i:s')
                ]);
            } else {
                $nft_pk_id = $find['id'];
                Db::name('nft')->where('id', $nft_pk_id)->update(['updated_at' => date('Y-m-d H:i:s')]);
            }

            for ($i = 0; $i < $buy_count; $i++) {
                $value = Redis::incrBy($value_key, 1);
                // 开始创建nft_list记录
                if (!Db::name('nft_list')->insert([
                    'nft_pk_id' => $nft_pk_id,
                    'uid' => $uid,
                    'goods_id' => $redis_goods_info['id'],
                    'name' => $redis_goods_info['name'],
                    'value' => $value,
                    'pic' => $redis_goods_info['pic'],
                    'hash' => $redis_goods_info['pic_hash'],
                    'order_pk_id' => $orderInfo['id'],
                    'prev_get_drop_time' => date('Y-m-d H:i:s'),
                    'created_at' => date('Y-m-d H:i:s'),
                    'updated_at' => date('Y-m-d H:i:s')
                ])) {
                    throw new AdminException("网络错误请稍后重试！");
                }
            }

            $updateData = [
                'name' => $orderUser['name'], // 用户姓名
                'paid' => 1, // 支付状态
                'pay_time' => date('Y-m-d H:i:s'),// 支付时间
                'pay_time_number' => time(),// 支付时间 时间戳
                'status' => 1, // 订单状态（0 未付款 1 已付款未到账 2 已付款已到账 3 已退款 4 订单超时）
                'updated_at' => date('Y-m-d H:i:s')
            ];

            $res = Db::name('store_order')->where('id', $orderInfo['id'])->update($updateData);

            if (!$res) {
                throw new AdminException("网络错误请稍后重试！");
            }

            // 提交事务
            Db::commit();
        } catch (\Exception $e) {
            // 回滚事务
            Db::rollback();
            throw new AdminException($e->getMessage());
        }

        # 藏品更新后的信息存入redis 同步
        $goodsInfo = Db::name('goods')->where('id', $goods_id)->find();

        # 兜底
        $j_num = $goodsInfo['current_have_num'];
        if ($j_num < 0) {
            Db::name('goods')->where('id', $goods_id)->update([
                'current_have_num' => 0
            ]);
        }
        $j_sold = $goodsInfo['sold'];
        if ($j_sold > $redis_goods_info['num']) {
            Db::name('goods')->where('id', $goods_id)->update([
                'sold' => $redis_goods_info['num']
            ]);
        }

        Redis::set($prefix . 'goods_id_' . $goods_id, json_encode($goodsInfo));

        return true;
    }

    /**
     * 订单查询后的 回调成功通知
     * @param int $order_pk_id
     * @return bool
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function selectPayNotify(int $order_pk_id)
    {
        $lock = Redis::setNx('order_pk_id:' . $order_pk_id, 1); // 只能是一个线程 会 变为 true

        if (!empty($lock)) {
            $orderInfo = Db::name('store_order')->where('id', $order_pk_id)->find();

            if (!$orderInfo) {
                Redis::del('order_pk_id:' . $order_pk_id);
                return true; // 订单不存在
            }

            $goods_id = $orderInfo['goods_id'];
            $uid = $orderInfo['uid'];

            // 订单 用户信息
            $orderUser = Db::name('user')->where('id', $uid)->find();

            // 藏品信息
            $redis_goods_info = $this->getGoodsInfo($orderInfo['goods_id']);

            if ($orderInfo['status'] == 1 || $orderInfo['status'] == 2) {
                return true; // 订单已支付
            }
            if ($orderInfo['status'] == 4) {
                return true; // 订单已超时
            }

            // 用户信息
            $redis_user_info = $this->getUserInfo($orderInfo['uid']);

//        if (empty($redis_user_info['account_id'])) {
//            //throw new AdminException('您暂未区块链账户，请去实名认证页面查看并获取！');
//        }

            $prefix = env('REDIS_PREFIX');
            //$ttl_seconds = env('REDIS_TTL_SECONDS');

            $find = Db::name('nft')->where('uid', $uid)->where('goods_id', $redis_goods_info['id'])->find();

            //开始事务
            Db::startTrans();

            try {

//            $key = $prefix . $goods_id . '_ex_goods_id_num';
                // 编号 key redis计数
                $value_key = $prefix . 'goods_' . $goods_id . '_number';
                $buy_count = $orderInfo['buy_count']; // 购买数量

                # 减库存
                if (!Db::name('goods')->where('id', $goods_id)->dec('current_have_num', $buy_count)->update()) {
                    throw new AdminException("网络错误请稍后重试！");
                }

                // 加已售数量
                if (!Db::name('goods')->where('id', $goods_id)->inc('sold', $buy_count)->update()) {
                    throw new AdminException("网络错误请稍后重试！");
                }

                if (!$find) {
                    $nft_pk_id = Db::name('nft')->insertGetId([
                        'uid' => $uid,
                        'mobile' => $redis_user_info['mobile'],
                        'account_id' => $redis_user_info['account_id'],
                        'goods_id' => $redis_goods_info['id'],
                        'goods_name' => $redis_goods_info['name'],
                        'created_at' => date('Y-m-d H:i:s'),
                        'updated_at' => date('Y-m-d H:i:s')
                    ]);
                } else {
                    $nft_pk_id = $find['id'];
                    Db::name('nft')->where('id', $nft_pk_id)->update(['updated_at' => date('Y-m-d H:i:s')]);
                }

                for ($i = 0; $i < $buy_count; $i++) {
                    $value = Redis::incrBy($value_key, 1);
                    // 开始创建nft_list记录
                    if (!Db::name('nft_list')->insert([
                        'nft_pk_id' => $nft_pk_id,
                        'uid' => $uid,
                        'goods_id' => $redis_goods_info['id'],
                        'name' => $redis_goods_info['name'],
                        'value' => $value,
                        'pic' => $redis_goods_info['pic'],
                        'hash' => $redis_goods_info['pic_hash'],
                        'order_pk_id' => $orderInfo['id'],
                        'prev_get_drop_time' => date('Y-m-d H:i:s'),
                        'created_at' => date('Y-m-d H:i:s'),
                        'updated_at' => date('Y-m-d H:i:s')
                    ])) {
                        throw new AdminException("网络错误请稍后重试！");
                    }
                }

                $updateData = [
                    'name' => $orderUser['name'], // 用户姓名
                    'paid' => 1, // 支付状态
                    'pay_time' => date('Y-m-d H:i:s'),// 支付时间
                    'pay_time_number' => time(),// 支付时间 时间戳
                    'status' => 1, // 订单状态（0 未付款 1 已付款未到账 2 已付款已到账 3 已退款 4 订单超时）
                    'updated_at' => date('Y-m-d H:i:s')
                ];

                $res = Db::name('store_order')->where('id', $orderInfo['id'])->update($updateData);

                if (!$res) {
                    throw new AdminException("网络错误请稍后重试！");
                }

                // 提交事务
                Db::commit();
            } catch (\Exception $e) {
                // 回滚事务
                Db::rollback();
                Redis::del('order_pk_id:' . $order_pk_id);
                throw new AdminException($e->getMessage());
            }

            # 藏品更新后的信息存入redis 同步
            $goodsInfo = Db::name('goods')->where('id', $goods_id)->find();

            # 兜底
            $j_num = $goodsInfo['current_have_num'];
            if ($j_num < 0) {
                Db::name('goods')->where('id', $goods_id)->update([
                    'current_have_num' => 0
                ]);
            }
            $j_sold = $goodsInfo['sold'];
            if ($j_sold > $redis_goods_info['num']) {
                Db::name('goods')->where('id', $goods_id)->update([
                    'sold' => $redis_goods_info['num']
                ]);
            }

            Redis::set($prefix . 'goods_id_' . $goods_id, json_encode($goodsInfo));
        }

        return true;
    }

    /**
     * 杉德收银台H5支付回调 压力测试
     * @param string $trade_no 拿到订单表 主键id
     * @return bool
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function payNotifyTest(string $trade_no)
    {
        $orderInfo = Db::name('store_order')->where('id', $trade_no)->find();
        if (!$orderInfo) {
            return true; // 订单不存在
        }
        $goods_id = $orderInfo['goods_id'];
        $uid = $orderInfo['uid'];
        // 订单 用户信息
        $orderUser = Db::name('user')->where('id', $uid)->find();
        // 藏品信息
        $redis_goods_info = $this->getGoodsInfo($goods_id);

        if ($orderInfo['status'] == 1 || $orderInfo['status'] == 2) {
            return true; // 订单已支付
        }
        if ($orderInfo['status'] == 4) {
            return true; // 订单已超时
        }
        // 用户信息
        $redis_user_info = $this->getUserInfo($uid);
//        if (empty($redis_user_info['account_id'])) {
        //throw new AdminException('您暂未区块链账户，请去实名认证页面查看并获取！');
//        }

        $prefix = env('REDIS_PREFIX');
        //$ttl_seconds = env('REDIS_TTL_SECONDS');

        $find = Db::name('nft')->where('uid', $uid)->where('goods_id', $redis_goods_info['id'])->find();

        //开始事务
        Db::startTrans();

        try {
//            $key = $prefix . $goods_id . '_ex_goods_id_num';
            // 编号 key redis计数
            $value_key = $prefix . 'goods_' . $goods_id . '_number';
            $buy_count = $orderInfo['buy_count']; // 购买数量

            # 减库存
            if (!Db::name('goods')->where('id', $goods_id)->dec('current_have_num', $buy_count)->update()) {
                throw new AdminException("网络错误请稍后重试！");
            }

            // 加已售数量
            if (!Db::name('goods')->where('id', $goods_id)->inc('sold', $buy_count)->update()) {
                throw new AdminException("网络错误请稍后重试！");
            }

            if (!$find) {
                $nft_pk_id = Db::name('nft')->insertGetId([
                    'uid' => $uid,
                    'mobile' => $redis_user_info['mobile'],
                    'account_id' => $redis_user_info['account_id'],
                    'goods_id' => $redis_goods_info['id'],
                    'goods_name' => $redis_goods_info['name'],
                    'created_at' => date('Y-m-d H:i:s'),
                    'updated_at' => date('Y-m-d H:i:s')
                ]);
            } else {
                $nft_pk_id = $find['id'];
                Db::name('nft')->where('id', $nft_pk_id)->update(['updated_at' => date('Y-m-d H:i:s')]);
            }

            for ($i = 0; $i < $buy_count; $i++) {
                $value = Redis::incrBy($value_key, 1);
                // 开始创建nft_list记录
                if (!Db::name('nft_list')->insert([
                    'nft_pk_id' => $nft_pk_id,
                    'uid' => $uid,
                    'goods_id' => $redis_goods_info['id'],
                    'name' => $redis_goods_info['name'],
                    'value' => $value,
                    'pic' => $redis_goods_info['pic'],
                    'hash' => $redis_goods_info['pic_hash'],
                    'order_pk_id' => $orderInfo['id'],
                    'prev_get_drop_time' => date('Y-m-d H:i:s'),
                    'created_at' => date('Y-m-d H:i:s'),
                    'updated_at' => date('Y-m-d H:i:s')
                ])) {
                    throw new AdminException("网络错误请稍后重试！");
                }
            }

            $updateData = [
                'name' => $orderUser['name'], // 用户姓名
                'paid' => 1, // 支付状态
                'pay_time' => date('Y-m-d H:i:s'),// 支付时间
                'pay_time_number' => time(),// 支付时间 时间戳
                'status' => 1, // 订单状态（0 未付款 1 已付款未到账 2 已付款已到账 3 已退款 4 订单超时）
                'updated_at' => date('Y-m-d H:i:s')
            ];

            $res = Db::name('store_order')->where('id', $orderInfo['id'])->update($updateData);

            if (!$res) {
                throw new AdminException("网络错误请稍后重试！");
            }

            // 提交事务
            Db::commit();
        } catch (\Exception $e) {
            // 回滚事务
            Db::rollback();
            throw new AdminException($e->getMessage());
        }

        # 藏品更新后的信息存入redis 同步
        $goodsInfo = Db::name('goods')->where('id', $goods_id)->find();

        # 兜底
        $j_num = $goodsInfo['current_have_num'];
        if ($j_num < 0) {
            Db::name('goods')->where('id', $goods_id)->update([
                'current_have_num' => 0
            ]);
        }
        $j_sold = $goodsInfo['sold'];
        if ($j_sold > $redis_goods_info['num']) {
            Db::name('goods')->where('id', $goods_id)->update([
                'sold' => $redis_goods_info['num']
            ]);
        }

        Redis::set($prefix . 'goods_id_' . $goods_id, json_encode($goodsInfo));

        return true;
    }

    /**
     * 发送订单查询的延迟队列 时间 20秒 = 20000
     * @param $id
     * @param $ttl
     * @return bool
     * @throws Exception
     */
    public function sendSelectOrderMsg($id, $ttl = 300000)
    {
        $v_host = env('RABBITMQ_VHOST');
        $exc_name = 'delay_exc_select_order';
        $routing_key = 'delay_select_order';
        $queue_name = 'delay_queue_select_order';

//        $ttl = 300000; // 300秒 5分钟

        $connection = new AMQPStreamConnection('localhost', 5672, env('RABBITMQ_USER'), env('RABBITMQ_PWD'), $v_host);

        $channel = $connection->channel();
        $channel->exchange_declare($exc_name, 'x-delayed-message', false, true, false);
        $args = new AMQPTable(['x-delayed-type' => 'direct']);
        $channel->queue_declare($queue_name, false, true, false, false, false, $args);
        $channel->queue_bind($queue_name, $exc_name, $routing_key);

        $data = $id;

        $arr = ['delivery_mode' => AMQPMEssage::DELIVERY_MODE_PERSISTENT, 'application_headers' => new AMQPTable(['x-delay' => $ttl])];
        $msg = new AMQPMessage($data, $arr);
        $channel->basic_publish($msg, $exc_name, $routing_key);
        $channel->close();
        $connection->close();
        return true;
    }

    /**
     * 发送藏品购买成功压测通知的延迟队列 时间 3秒 = 3000
     * @param $id
     * @param $ttl
     * @return bool
     * @throws Exception
     */
    public function sendGoodsNotifyTestMsg($id, $ttl = 3000)
    {
        $v_host = env('RABBITMQ_VHOST');
        $exc_name = 'delay_exc_goods_notify_test';
        $routing_key = 'delay_goods_notify_test';
        $queue_name = 'delay_queue_goods_notify_test';

//        $ttl = 300000; // 300秒 5分钟

        $connection = new AMQPStreamConnection('localhost', 5672, env('RABBITMQ_USER'), env('RABBITMQ_PWD'), $v_host);

        $channel = $connection->channel();
        $channel->exchange_declare($exc_name, 'x-delayed-message', false, true, false);
        $args = new AMQPTable(['x-delayed-type' => 'direct']);
        $channel->queue_declare($queue_name, false, true, false, false, false, $args);
        $channel->queue_bind($queue_name, $exc_name, $routing_key);

        $data = $id;

        $arr = ['delivery_mode' => AMQPMEssage::DELIVERY_MODE_PERSISTENT, 'application_headers' => new AMQPTable(['x-delay' => $ttl])];
        $msg = new AMQPMessage($data, $arr);
        $channel->basic_publish($msg, $exc_name, $routing_key);
        $channel->close();
        $connection->close();
        return true;
    }

    /**
     * 检测 用户 针对 某个藏品 单人 可购买数量 和 单人 可拥有数量 是否满足限制条件 和 是否达到开售时间
     * @param int $uid
     * @param int $goods_id
     * @param int $buy_count 购买数量
     * @param int $is_cx_buy 是否重新下单
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function detectionCount(int $uid, int $goods_id, int $buy_count, int $is_cx_buy = 0)
    {

        // 用户信息
        $redis_user_info = $this->getUserInfo($uid);
//        if (empty($redis_user_info['account_id'])) {
//            //throw new AdminException('您暂未区块链账户，请去实名认证页面查看并获取！');
//        }

        // 藏品信息
        $redis_goods_info = $this->getGoodsInfo($goods_id);

        if ($redis_goods_info['status'] < 1) {
            throw new AdminException('此藏品已下架！');
        }

        // is_rights1  是否拥有优先购权限   0 否  1  是
        if ($redis_goods_info['is_rights1'] == 0) {
            if (strtotime($redis_goods_info['issuer_date']) > time()) {
                throw new AdminException('此藏品暂未到开售时间，请您耐心等待！');
            }
        }

        $can_buy_num = $redis_goods_info['can_buy_num']; // 单人 可购买数量
        $can_have_num = $redis_goods_info['can_have_num']; // 单人 可拥有数量

//        $where = [];
//        $where[] = ['type', '=', 0]; // 类型是购买藏品的
//        $where[] = ['uid', '=', $uid]; // 购买人是我
//        $where[] = ['goods_id', '=', $goods_id]; // 藏品id就是本次下单要买的藏品id
//        $where[] = ['status', 'in', '0,1,2,5']; // 订单状态（0 支付中 1 已付款未到账 2 已付款已到账 3 已退款 4 订单超时 5 支付失败（或者待支付））
////        $my_buy_count = $this->dao->getCount($where) ?? 0;
//        $my_buy_count = $this->dao->sum($where, 'buy_count') ?? 0;

        # 强制索引

        $my_buy_count = Db::name('store_order')
                ->force('idx_type_uid_goodsid_status')
                ->where('type', 0)
                ->where('uid', $uid)
                ->where('goods_id', $goods_id)
                ->where('status', 'in', '0,1,2,5')
                ->sum('buy_count') ?? 0;

        $my_buy_count = intval($my_buy_count);

        // is_rights1  是否拥有优先购权限   0 否  1  是
        if ($redis_goods_info['is_rights1'] == 1) {
            if (strtotime($redis_goods_info['issuer_date']) > time()) {


                if (strtotime($redis_goods_info['priority_purchase_time']) > time()) {
                    throw new AdminException('优先购时间还未开始！');
                }

                if (strtotime($redis_goods_info['priority_end_time']) <= time()) {
                    throw new AdminException('优先购时间已截止！');
                }

                // 此藏品暂未到开售时间  查询 用户 优先购数量

                $my_yxg_all_count = Db::name('yxg_count')
                        ->force('idx_gid_uid_mobile')
                        ->where('goods_id', $goods_id)
                        ->where('uid', $uid)
                        ->sum('num') ?? 0; // 我的这个藏品的优先购总份数

                if ($my_yxg_all_count == 0) {
                    throw new AdminException('此藏品您的优先购份数为0，无法提前购买！');
                }

                if ($is_cx_buy == 1) {
                    $my_buy_count = $my_buy_count - $buy_count;
                }

                $remain_yxg_count = $my_yxg_all_count - $my_buy_count; // 我的 剩余优先购数量

                if ($buy_count > $remain_yxg_count) {
                    throw new AdminException('此藏品您的优先购剩余份数为' . $remain_yxg_count . '份，无法提前购买' . $buy_count . '份！');
                }

            }
        }

        if ($redis_goods_info['is_rights1'] == 0) {

//            halt($my_buy_count);
            if ($is_cx_buy == 1) {
                $my_buy_count = $my_buy_count - $buy_count;
            }

            if ($my_buy_count >= $can_buy_num) {
                throw new AdminException('此藏品单人可下单数量是' . $can_buy_num . '个！');
            }

            $my_buy_count = $my_buy_count + $buy_count;
//            var_dump($my_buy_count);
//            var_dump($can_buy_num);
//            die;

            if ($my_buy_count > $can_buy_num) {
                throw new AdminException('此藏品单人可下单数量是' . $can_buy_num . '个！');
            }

            $my_nft_count = Db::name('nft_list')->where(['uid' => $uid, 'goods_id' => $goods_id, 'status' => 'Active'])->count() ?? 0; // status : Active活跃 Burned销毁

            if ($my_nft_count >= $can_have_num) {
                throw new AdminException('此藏品单人可拥有数量是' . $can_have_num . '个，您已拥有' . $my_nft_count . '个！');
            }


            $my_nft_count = $my_nft_count + $buy_count;

            if ($my_nft_count > $can_have_num) {
                throw new AdminException('此藏品单人可拥有数量是' . $can_have_num . '个，您已拥有' . $my_nft_count . '个！');
            }
        }

        return [
            'redis_user_info' => $redis_user_info,
            'redis_goods_info' => $redis_goods_info
        ];
    }

    /**
     * 返回用户信息
     * @param int $uid
     * @return mixed
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function getUserInfo(int $uid)
    {
        $prefix = env('REDIS_PREFIX');

        $user_key = $prefix . 'user_id_' . $uid;
        $user_is_exists = Redis::exists($user_key);
        if (!$user_is_exists) {
            $userInfo = Db::name('user')->where('id', $uid)->find(); // 用户信息
            $userData = [
                'user_id' => $uid,
                'account_id' => $userInfo['account_id'],
                'mobile' => $userInfo['mobile']
            ];
            Redis::set($user_key, json_encode($userData));
            Redis::set($prefix . 'user_mobile_' . $userInfo['mobile'], json_encode($userData));
        }

        $redis_user = Redis::get($user_key);
        $redis_user_info = json_decode($redis_user, true);
        return $redis_user_info;
    }

    /**
     * 获取藏品信息
     * @param int $goods_id
     * @return mixed
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function getGoodsInfo(int $goods_id)
    {
        $prefix = env('REDIS_PREFIX');

        $goods_key = $prefix . 'goods_id_' . $goods_id;
        $goods_is_exists = Redis::exists($goods_key);
        if (!$goods_is_exists) {
            $goodsInfo = Db::name('goods')->where('id', $goods_id)->find();
            Redis::set($goods_key, json_encode($goodsInfo));
        }

        $redis_goods = Redis::get($goods_key);
        $redis_goods_info = json_decode($redis_goods, true);
        return $redis_goods_info;
    }

    /**
     * @return string
     */
    public function orderId()
    {
        return date('Ymd') . substr(implode(null, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
    }
}
